#!/usr/bin/env python
# coding: utf-8
# Copyright (c) 2024 Huawei Technologies Co., Ltd.
# openUBMC is licensed under Mulan PSL v2.
# You can use this software according to the terms and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
#         http://license.coscl.org.cn/MulanPSL2
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PSL v2 for more details.

import os
import sys
import shlex
import logging
import argparse
import tempfile
import shutil
import glob
import subprocess
import getpass
from multiprocessing import Process

HTTPS_PROXY_STR = "https_proxy"


class Task(Process):
    @staticmethod
    def log_init():
        logger = logging.getLogger()
        logger.setLevel(logging.INFO)
        loglevel = os.environ.get("LOG")
        if loglevel is not None:
            formatter = logging.Formatter(fmt='[{asctime} {levelname} {filename}:{lineno} {funcName:4}] {message}',
                                          style="{")
            if loglevel == "debug":
                logger.setLevel(logging.DEBUG)
            elif loglevel == "warn":
                logger.setLevel(logging.WARNING)
            elif loglevel == "error":
                logger.setLevel(logging.ERROR)
            else:
                logger.setLevel(logging.INFO)
        else:
            formatter = logging.Formatter(fmt='[{levelname}] {message}',
                                          style='{')
            handler = logging.StreamHandler(sys.stdout)
            handler.setFormatter(formatter)
            logger.handlers = []
            logger.addHandler(handler)

    @staticmethod
    def format_command(command, sudo=False):
        cmd_args: list[str] = []
        if isinstance(command, list):
            cmd_args = command
        elif isinstance(command, str):
            cmd_args = shlex.split(command)
            cmd_args[0] = shutil.which(cmd_args[0])
            if cmd_args[0] is None:
                raise Exception(f"{cmd_args}不存在，请检查命令或环境配置")
        else:
            raise Exception("Command with invalid instance")
        sudo_cmd = shutil.which("sudo")
        if sudo and sudo_cmd and cmd_args[0] != sudo_cmd:
            cmd_args.insert(0, sudo_cmd)
        return cmd_args
    
    def chdir(self, path):
        logging.info(f"chdir: {path}")
        os.chdir(path)

    def run_command(self, command, stdout="", check=True, sudo=False, **kwargs):
        command_echo = kwargs.get("command_echo", True)
        capture_output = kwargs.get("capture_output", False)
        if command_echo:
            logging.info(f">>>> {command}")
        command = self.format_command(command, sudo)
        if stdout:
            return subprocess.run(command, check=check, timeout=600, stdout=stdout)
        else:
            return subprocess.run(command, check=check, timeout=600, capture_output=capture_output)


class WorkPrepare(Task):
    def __init__(self):
        super().__init__()
        parser = argparse.ArgumentParser(
            prog="openUBMC Development environment initialization",
            formatter_class=argparse.RawTextHelpFormatter
        )
        parser.add_argument("-path", "--bmc_sdk_path", help="bmcsdk压缩文件路径，该文件可从openUBMC社区的应用市场下载", required=True)
        parser.add_argument("-user", "--openubmc_user", help="openUBMC社区用户名", required=False, default="")
        parser.add_argument("-psw", "--openubmc_password", help="openUBMC社区用户密码，推荐使用交互式输入密码",
                            required=False, default="")
        parsed_args, _ = parser.parse_known_args(sys.argv)

        self.home = os.environ["HOME"]
        self.cwd = os.getcwd()
        os.environ["DEBIAN_FRONTEND"] = "noninteractive"
        # 非root用户设置环境变量
        path = os.environ.get("PATH")
        user_bin_path = f"{self.home}/.local/bin"
        self.need_add_user_bin_path = False
        if self.home.startswith("/home") and user_bin_path not in path:
            self.need_add_user_bin_path = True
            path = f"{user_bin_path}:" + path
            os.environ["PATH"] = path

        os.environ["AUTO_ACCEPT_LICENSE"] = "true"
        self.bmc_sdk_path = parsed_args.bmc_sdk_path
        self.user = parsed_args.openubmc_user 
        self.password = parsed_args.openubmc_password 

    def proxy_check(self):
        https_proxy = os.environ.get(HTTPS_PROXY_STR)
        if https_proxy:
            logging.warning("检测到环境设置了https_proxy代理，可能导致初始化失败，请确认是否关闭代理运行？(Y/N)")
            if input().upper() == "Y":
                os.environ[HTTPS_PROXY_STR] = ""
                os.environ[HTTPS_PROXY_STR.upper()] = ""

    def environ_check(self):
        sudouser = os.environ.get("SUDO_USER")
        if sudouser is not None:
            raise Exception("请直接执行，不支持以sudo环境执行脚本")
        with open("/etc/issue", "r") as fq:
            issue = fq.readline().strip()
            if not issue.startswith("Ubuntu 24.04"):
                raise Exception(f"仅支持Ubuntu 24.04操作系统，当前系统为 {issue}")

    def ubuntu_init(self):
        self.run_command("ln -snf /bin/bash /bin/sh", sudo=True)
        logging.info("Initial ubuntu env")
        self.run_command(
            f"sed -i 's/archive.ubuntu/mirrors.huaweicloud/g' /etc/apt/sources.list.d/ubuntu.sources", 
            sudo=True, check=False
        )
        self.run_command(
            f"sed -i 's/security.ubuntu/mirrors.huaweicloud/g' /etc/apt/sources.list.d/ubuntu.sources", 
            sudo=True, check=False
        )
        self.run_command("apt-get update", sudo=True)

    def install_dependencies(self):
        logging.info("Start installing the dependencies：")
        self.run_command(
            f"apt-get install -y python3 python3-pip python3-dev git wget curl ca-certificates \
            ipmitool dbus-x11 libdbus-1-dev dbus \
            pkgconf libglib2.0-dev gcc-9 g++-9 unzip npm", 
            sudo=True
        )
        self.run_command(
            f"update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 20 \
            --slave /usr/bin/g++ g++ /usr/bin/g++-9 --slave /usr/bin/gcov gcov /usr/bin/gcov-9", 
            sudo=True
        )
        self.run_command(f"apt remove -y python3-urllib3 python3-distro", sudo=True)
        self.run_command(f"apt autoremove -y", sudo=True)

    def pip_config(self):
        logging.info("Start configuring pip：")
        self.run_command(f"pip config set global.index-url https://mirrors.huaweicloud.com/repository/pypi")
        self.run_command(f"pip config set global.extra-index-url https://mirrors.huaweicloud.com/repository/pypi/simple")
        self.run_command(f"pip config set global.trusted-host 'mirrors.huaweicloud.com'")
        self.run_command("pip3 config set global.break-system-packages true")

    def install_old_openssl(self):
        logging.info("Start installing the openssl：")
        tmpdir = tempfile.TemporaryDirectory()
        self.chdir(tmpdir.name)
        self.run_command(
            f"wget https://mirrors.huaweicloud.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2_amd64.deb"
        )
        self.run_command(f"dpkg -i libssl1.1_1.1.1f-1ubuntu2_amd64.deb", sudo=True)
        os.chdir(self.cwd)

    def install_dev_tools(self):
        logging.info("Start installing the tools：")
        real_path = os.path.join(self.cwd, self.bmc_sdk_path)
        tmpdir = tempfile.TemporaryDirectory()
        self.chdir(tmpdir.name)
        self.run_command(f"unzip {real_path}")
        ret = self.run_command(f"rm -rf {self.home}/rtos_compiler", capture_output=True, check=False)
        if ret.returncode != 0:
            self.run_command(f"rm -rf {self.home}/rtos_compiler", sudo=True)
        ret = self.run_command(f"rm -rf {self.home}/sdk", capture_output=True, check=False)
        if ret.returncode != 0:
            self.run_command(f"rm -rf {self.home}/sdk", sudo=True)
        self.run_command(f"cp -rf rtos_compiler {self.home}")
        self.run_command(f"cp -rf sdk {self.home}")
        self.run_command(f"cp -rf sdk/hi1711sdk.tar.gz {self.home}/rtos_compiler")
        self.run_command(f"cp -rf lua-format /usr/bin", sudo=True)
        self.run_command(f"chmod +x /usr/bin/lua-format", sudo=True)
        self.run_command(f"chmod -R +x hpm_tools", sudo=True)
        self.run_command(f"cp -rf hpm_tools/. /usr/bin", sudo=True)
        os.chdir(self.cwd)

    def install_node(self):
        logging.info("Start installing node：")
        self.run_command(f"npm install -g n", sudo=True)
        self.run_command(f"n 20.18.0", sudo=True)
        self.run_command(f"npm install -g npm@10.8.2", sudo=True)

    def install_bingo(self): 
        stop_ret = self.run_command("apt policy openubmc-bingo", capture_output=True)
        if len(stop_ret.stdout) == 0:
            self.apt_config()
        try:
            self.run_command("pip install conan>=2.0.0")
            self.run_command("apt install -y openubmc-bingo --upgrade", sudo=True)
            self.run_command("pip install openubmc-bingo --upgrade")
        except Exception:
            logging.info(f"openubmc-bingo下载失败，请检查apt源是否配置成功")

    def install_studio(self):
        tmpdir = tempfile.TemporaryDirectory()
        self.chdir(tmpdir.name)
        try:
            self.run_command(f"wget https://repo.openubmc.cn/latest/tools/BMC%20Studio.zip -O studio.zip")
        except Exception as e:
            raise e
        try:
            self.run_command(f"unzip ./studio.zip")
        except Exception as e:
            raise e
        deb_file = glob.glob(os.path.join(os.getcwd(), "*.deb"))[0]
        if not deb_file:
            raise Exception(f"未找到BMC Studio工具的安装包，无法进行安装")
        try:
            self.run_command(f"dpkg -i {deb_file}", sudo=True)
        except subprocess.CalledProcessError as e:
            self.run_command("apt install -y -f", sudo=True)
            self.run_command(f"dpkg -i {deb_file}", sudo=True)
        os.chdir(self.cwd)

    def install_cangjie(self):
        try:
            stop_ret = self.run_command("apt policy cangjie-runtime", capture_output=True, sudo=True)
            if len(stop_ret.stdout) == 0:
                self.apt_config()
            self.run_command(f"apt install -y cangjie-runtime", sudo=True)
        except Exception as e:
            raise e

    def apt_config(self):
        key_id = "BF3B2D407FDEDEA0DC91CC9412640FBCB175287A"
        keyrings_dir = "/usr/share/keyrings"
        gpg_path = f"{keyrings_dir}/openubmc.gpg"
        apt_source = f"""deb [arch=amd64 signed-by={gpg_path}] \
            https://openubmc-apt-repo.obs.cn-north-4.myhuaweicloud.com/Ubuntu/ noble main"""
        sources_list = "/etc/apt/sources.list.d/openubmc.list"
        try: 
            os.makedirs(keyrings_dir, exist_ok=True, mode=0o755)
            self.run_command(f"gpg --keyserver keyserver.ubuntu.com --recv-keys {key_id}", sudo=True)

            with open(gpg_path, "wb") as f:
                self.run_command(f"gpg --export {key_id}", check=True, stdout=f)
            os.chmod(gpg_path, 0o644)

            # 配置APT源
            logging.info(f"Writing APT source to {sources_list}...")
            with open(sources_list, "w") as f:
                f.write(apt_source + "\n")
            self.run_command("apt update")
        except subprocess.CalledProcessError as e:
            logging.error(f"Command failed: {' '.join(e.cmd)}\nError: {e.stderr}")
        except Exception as e:
            logging.error(f"Error: {str(e)}")

    def conan_config(self):
        logging.info("Start configuring conan:")
        self.run_command("conan profile detect --force")
        self.run_command("conan remote remove conancenter", check=False)
        self.run_command("conan remote add openubmc_dev 'https://conan.openubmc.cn/conan_1/' --insecure --force")
        if not self.user:
            conan_usr = input("请输入conan仓的用户名: ").strip()
        else:
            conan_usr = self.user
        if not self.password:
            conan_psw = getpass.getpass(prompt="请输入conan仓的密码: ")
        else:
            conan_psw = self.password
        self.run_command(f"conan remote login openubmc_dev {conan_usr} -p {conan_psw}", command_echo=False)

    def install_sdk(self):
        logging.info("Start installing sdk：")
        os.chdir(self.cwd)
        self.run_command("bingo build -t install_sdk -b openUBMC -ucc")

    def install_qemu(self):
        stop_ret = self.run_command("apt policy openubmc-qemu", capture_output=True)
        if len(stop_ret.stdout) == 0:
            self.apt_config()
        try:
            self.run_command("apt install -y openubmc-qemu --upgrade", sudo=True)
        except Exception:
            logging.info(f"openubmc-qemu下载失败, 请检查apt源是否配置成功")

    def install_qemu_compile_dependencies(self):
        self.run_command(
            f"apt-get install -y make bzip2 pkg-config libslirp-dev libpixman-1-dev ninja-build \
            libcap-ng-dev libfdt-dev libattr1-dev", sudo=True)

    def install_qemu_runtime_dependencies(self):
        self.run_command("cp /etc/apt/sources.list.d/ubuntu.sources \
                         /etc/apt/sources.list.d/ubuntu.sources.bak", sudo=True)
        self.run_command("sed -i 's/Suites:/Suites: plucky/g' /etc/apt/sources.list.d/ubuntu.sources", sudo=True)
        self.run_command("apt-get update", sudo=True)
        self.run_command("apt-get install -y libcapstone5", sudo=True)
        self.run_command("rm /etc/apt/sources.list.d/ubuntu.sources", sudo=True)
        self.run_command("mv /etc/apt/sources.list.d/ubuntu.sources.bak \
                         /etc/apt/sources.list.d/ubuntu.sources", sudo=True)
        self.run_command("apt-get update", sudo=True)

    def install_studio_runtime_dependencies(self):
        try:
            self.run_command("apt-get install -y wslu xdg-utils nginx", sudo=True)
        except Exception as e:
            raise e
        self.install_cangjie()

    def check_path(self):
        if self.need_add_user_bin_path:
            logging.warning("检测到当前用户为非root用户吗，且未配置环境变量！！")
            logging.warning("请在初始化后在终端依次执行以下两条命令：")
            logging.warning("echo 'export PATH=\"$HOME/.local/bin:$PATH\"' >> ~/.bashrc")
            logging.warning("source ~/.bashrc")
            logging.warning("请确认已知悉，输入Y:")
            if input().upper() == "Y":
                return

    def run(self):
        try:
            self.proxy_check()
            self.environ_check()
            self.ubuntu_init()
            self.install_dependencies()
            self.install_old_openssl()
            self.pip_config()
            self.install_bingo()
            self.conan_config()
            self.install_dev_tools()
            self.install_node()
            self.install_sdk()
            # 初始化qemu编译和运行时依赖
            logging.info("开始安装qemu的deb包和qemu依赖环境: ")
            self.install_qemu_compile_dependencies()
            self.install_qemu_runtime_dependencies()
            self.install_qemu()
            logging.info("qemu依赖环境初始化成功")
            # 安装BMC Studio
            try:
                self.run_command("apt-get remove -y bmc-studio", sudo=True, check=False)
                self.install_studio_runtime_dependencies()
                self.install_studio()
                logging.info("BMC Studio安装成功")
            except Exception as exc:
                logging.error(str(exc))
            self.check_path()
            logging.info("openUBMC开发者环境初始化完成！")
        except Exception as exc:
            if os.environ.get("LOG"):
                import traceback
                logging.error(traceback.format_exc())
            msg = str(exc)
            logging.error(msg)
            return -1
        return 0

if __name__ == "__main__":
    wk = WorkPrepare()
    sys.exit(wk.run())
